package org.smallmind.scribe.pen;

import java.util.Arrays;
import java.util.LinkedList;
import org.smallmind.scribe.pen.nutsnbolts.lang.UnknownSwitchCaseException;

public abstract class Template {

   public final static int NO_MATCH = -1;

   public static enum Change {

      LEVEL, CONTEXT, FILTER, APPENDER, ENHANCER
   }

   private LinkedList<Filter> filterList;
   private LinkedList<Appender> appenderList;
   private LinkedList<Enhancer> enhancerList;
   private Level level = Level.INFO;
   private boolean autoFillLogicalContext = false;

   public Template () {

      filterList = new LinkedList<Filter>();
      appenderList = new LinkedList<Appender>();
      enhancerList = new LinkedList<Enhancer>();
   }

   public Template (Level level, boolean autoFillLogicalContext) {

      this();

      this.level = level;
      this.autoFillLogicalContext = autoFillLogicalContext;
   }

   public Template (Filter[] filters, Appender[] appenders, Enhancer[] enhancers, Level level, boolean autoFillLogicalContext) {

      this();

      filterList.addAll(Arrays.asList(filters));
      appenderList.addAll(Arrays.asList(appenders));
      enhancerList.addAll(Arrays.asList(enhancers));

      this.level = level;
      this.autoFillLogicalContext = autoFillLogicalContext;
   }

   public abstract int matchLogger (String loggerName);

   public synchronized void register () {

      LoggerManager.addTemplate(this);
   }

   public synchronized Level getLevel () {

      return level;
   }

   public synchronized void setLevel (Level level) {

      if (level == null) {
         throw new IllegalArgumentException("Can't set a 'null' default level");
      }

      this.level = level;
      LoggerManager.commitTemplateChanges(Change.LEVEL, this);
   }

   public synchronized boolean isAutoFillLogicalContext () {

      return autoFillLogicalContext;
   }

   public synchronized void setAutoFillLogicalContext (boolean autoFillLogicalContext) {

      this.autoFillLogicalContext = autoFillLogicalContext;
      LoggerManager.commitTemplateChanges(Change.CONTEXT, this);
   }

   public synchronized Filter[] getFilters () {

      Filter[] filters;

      filters = new Filter[filterList.size()];
      filterList.toArray(filters);

      return filters;
   }

   public synchronized void addFilter (Filter filter) {

      filterList.add(filter);
      LoggerManager.commitTemplateChanges(Change.FILTER, this);
   }

   public synchronized void removeFilter (Filter filter) {

      if (filterList.remove(filter)) {
         LoggerManager.commitTemplateChanges(Change.FILTER, this);
      }
   }

   public synchronized Appender[] getAppenders () {

      Appender[] appenders;

      appenders = new Appender[appenderList.size()];
      appenderList.toArray(appenders);

      return appenders;
   }

   public synchronized void addAppender (Appender appender) {

      appenderList.add(appender);
      LoggerManager.commitTemplateChanges(Change.APPENDER, this);
   }

   public synchronized void removeAppender (Appender appender) {

      if (appenderList.remove(appender)) {
         LoggerManager.commitTemplateChanges(Change.APPENDER, this);
      }
   }

   public synchronized Enhancer[] getEnhancers () {

      Enhancer[] enhancers;

      enhancers = new Enhancer[enhancerList.size()];
      enhancerList.toArray(enhancers);

      return enhancers;
   }

   public synchronized void addEnhancer (Enhancer enhancer) {

      enhancerList.add(enhancer);
      LoggerManager.commitTemplateChanges(Change.ENHANCER, this);
   }

   public synchronized void removeEnhancer (Enhancer enhancer) {

      if (enhancerList.remove(enhancer)) {
         LoggerManager.commitTemplateChanges(Change.ENHANCER, this);
      }
   }

   protected synchronized void apply (Logger logger) {

      for (Change change : Change.values()) {
         applyChange(change, logger);
      }
   }

   protected synchronized void applyChange (Change change, Logger logger) {

      switch (change) {

         case LEVEL:
            logger.setLevel(level);
            break;
         case CONTEXT:
            logger.setAutoFillLogigicalContext(autoFillLogicalContext);
            break;
         case FILTER:
            logger.clearFilters();
            for (Filter filter : filterList) {
               logger.addFilter(filter);
            }
            break;
         case APPENDER:
            logger.clearAppenders();
            for (Appender appender : appenderList) {
               logger.addAppender(appender);
            }
            break;
         case ENHANCER:
            logger.clearEnhancers();
            for (Enhancer enhancer : enhancerList) {
               logger.addEnhancer(enhancer);
            }
            break;
         default:
            throw new UnknownSwitchCaseException(change.name());
      }
   }
}
